/*
 * File:   AgentGenome.h
 * Author: root
 *
 * Created on November 20, 2008, 8:20 PM
 */

#ifndef _AGENTGENOME_H
#define	_AGENTGENOME_H

#include "ga/GAGenome.h"
#include "StrategyGene.h"
#include "StrategyCodec.h"
#include "Agent31.h"
#include "Query.h"
#include <string>
#include <sstream>
#include <cstdlib>

using namespace std;

enum EvalType {WR, SIR, TSA, SD, LSDA};

class AgentGenome : public GAGenome {

public:

    GADefineIdentity("AgentGenome", 555);
    static void Init(GAGenome&);
    static int Mutate(GAGenome&, float);
    static float Compare(const GAGenome&, const GAGenome&);
    static float EvaluateWR(GAGenome&);     // Win ratio
    static float EvaluateSIR(GAGenome&);    // Score increase rate
    static float EvaluateTSA(GAGenome&);    // Top score average
    static float EvaluateSD(GAGenome&);     // Score difference
    static float EvaluateLSDA(GAGenome&);    // Losing score difference average
    static int Cross(const GAGenome&, const GAGenome&, GAGenome*, GAGenome*);

protected:

    StrategyGene* geneArrayKnocking[AVAILABLE_KNOCKING];
    StrategyGene* geneArrayDrawing[AVAILABLE_DRAWING];
    StrategyGene* geneArrayDiscard[AVAILABLE_DISCARD];
    StrategyGene* geneArrayEndDrawing[AVAILABLE_DRAWING];
    StrategyGene** geneArray[4];
    static StrategyCodec codec;
    int numEvaluations;
    int matchesWon;
    int scoreAccum;
    float scoreDiffAccum;
    int losingScoreDiffAccum;
    float increaseRateAccum;
    int matchesPlayed;
    bool initialized;

    static int predefinedCounter;
    static const int predefinedLimit = 0;
    static const int crossoverRange = AVAILABLE_KNOCKING + 2*AVAILABLE_DRAWING + AVAILABLE_DISCARD;
    static const int crossMutationProb = 10;

    static void copyGene(const StrategyGene& original, StrategyGene*& copy) {
        int numParameters = original.getParametersSize();
        int geneParameters[numParameters];
        for (int i = 0; i < numParameters; i++) {
            geneParameters[i] = original.getParameter(i);
        }
        if (copy != NULL) {
            delete copy;
        }
        copy = new StrategyGene(original.getStrategyName(), original.getWeight(), geneParameters, numParameters);
    }

public:

    AgentGenome(EvalType eval=WR) : GAGenome(Init, Mutate, Compare) {
        switch (eval) {
            case WR: evaluator(EvaluateWR);
            break;
            case SIR: evaluator(EvaluateSIR);
            break;
            case TSA: evaluator(EvaluateTSA);
            break;
            case SD: evaluator(EvaluateSD);
            break;
            case LSDA: evaluator(EvaluateLSDA);
            break;
            default: evaluator(EvaluateWR);
        }
        crossover(Cross);
        numEvaluations = 0;
        matchesPlayed = 0;
        matchesWon = 0;
        scoreAccum = 0;
        scoreDiffAccum = 0;
        losingScoreDiffAccum = 0;
        increaseRateAccum = 0;
        initialized = false;
        geneArray[KNOCKING] = geneArrayKnocking;
        geneArray[DRAWING] = geneArrayDrawing;
        geneArray[DISCARD] = geneArrayDiscard;
        geneArray[END_DRAW] = geneArrayEndDrawing;
        for (int i = KNOCKING; i <= END_DRAW; i++) {
          int ai = (i == END_DRAW) ? DRAWING : i;
          for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
            geneArray[i][j] = NULL; // check this
          }
        }
    }

    AgentGenome(const AgentGenome& orig) {
        geneArray[KNOCKING] = geneArrayKnocking;
        geneArray[DRAWING] = geneArrayDrawing;
        geneArray[DISCARD] = geneArrayDiscard;
        geneArray[END_DRAW] = geneArrayEndDrawing;
        for (int i = KNOCKING; i <= END_DRAW; i++) {
          int ai = (i == END_DRAW) ? DRAWING : i;
          for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
            geneArray[i][j] = NULL; // check this
          }
        }
        initialized = false;
      copy(orig);
    }

    AgentGenome & operator=(const GAGenome& orig) {
        if (&orig != this) {
          copy(orig);
        }
        return *this;
    }

    virtual GAGenome* clone(CloneMethod) const {
        return new AgentGenome(*this); // careful with this?    Why??
    }

    virtual void copy(const GAGenome& orig) {
        GAGenome::copy(orig); // this copies all of the base genome parts
        const AgentGenome& agentGenome = dynamic_cast<const AgentGenome&>(orig);
        numEvaluations = agentGenome.numEvaluations;
        matchesPlayed = agentGenome.matchesPlayed;
        matchesWon = agentGenome.matchesWon;
        scoreAccum = agentGenome.scoreAccum;
        scoreDiffAccum = agentGenome.scoreDiffAccum;
        losingScoreDiffAccum = agentGenome.losingScoreDiffAccum;
        increaseRateAccum = agentGenome.increaseRateAccum;
        if (agentGenome.initialized) {
            for (int i = KNOCKING; i <= END_DRAW; i++) {
                int ai = (i == END_DRAW) ? DRAWING : i;
                for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
                    copyGene(*(agentGenome.geneArray[i][j]), geneArray[i][j]);
//                    StrategyGene* toCopy = agentGenome.geneArray[i][j];
//                    int geneParameters[toCopy->getParametersSize()];
//                    for (int k = 0; k < toCopy->getParametersSize(); k++) {
//                        geneParameters[k] = toCopy->getParameter(k);
//                    }
//                    if (geneArray[i][j] != NULL) {
//                        delete geneArray[i][j];
//                    }
//                    geneArray[i][j] = new StrategyGene(toCopy->getStrategyName(), toCopy->getWeight(), geneParameters, toCopy->getParametersSize());
                }
            }
            initialized = true;
        }
        else {
            initialized = false;
        }
    }

  // any data/member functions specific to this new class

    Agent31* buildAgent(int);

    static AgentGenome* createFromString(const string& compactString) {
        AgentGenome* newGenome = new AgentGenome();
        int start = 0;
        for (int i = KNOCKING; i <= END_DRAW; i++) {
            int ai = (i == END_DRAW) ? DRAWING : i;
            for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
                start = compactString.find("$", start) + 1;
                int next = compactString.find(":", start);
                string strategyName = compactString.substr(start, next-start);
                start = next + 1;
                next = compactString.find(":", start);
                int weight = atoi(compactString.substr(start, next-start).c_str());
                start = next + 1;
                next = compactString.find(":", start);
                int numParameters = atoi(compactString.substr(start, next-start).c_str());
                StrategyGene* newGene = new StrategyGene(strategyName, numParameters);
                newGene->setWeight(weight);
                for (int k = 0; k < numParameters; k++) {
                    start = next + 1;
                    next = compactString.find(":", start);
                    int param = atoi(compactString.substr(start, next-start).c_str());
                    newGene->setParameter(k, param);
                }
                newGenome->geneArray[i][j] = newGene;
            }
        }
        return newGenome;
    }

    float winRatio() const {
        float ret = 0;
        if (matchesPlayed > 0) {
            ret = (float) matchesWon / matchesPlayed;
        }
        return ret;
    }
    
    float scoreIncreaseRate() const {
        float ret = 0;
        if (matchesPlayed > 0) {
            ret = increaseRateAccum / matchesPlayed;
        }
        return ret;
    }
    
    float topScoreAverage() const {
        float ret = 0;
        if (matchesPlayed > 0) {
            ret = (float) scoreAccum / matchesPlayed;
        }
        return ret;
    }
    
    float scoreDiffHistory() const {
        return (float) scoreDiffAccum;
    }
    
    float losingScoreDiffAverage() const {
        float ret = 0;
        if (matchesPlayed > 0) {
            ret = (float) losingScoreDiffAccum / matchesPlayed;
        }
        return ret;
    }

    void recordMatch(bool won, int myScore, int oppScore, float incRate) {
        matchesPlayed++;
        scoreAccum += myScore;
        float scoreRatio = myScore / oppScore;
        scoreDiffAccum += scoreRatio;
        if (incRate > 0) {
            increaseRateAccum += incRate;
        }
        if (won) {
            matchesWon++;
        }
        else {
            int scoreDiff = oppScore - myScore;
            losingScoreDiffAccum += scoreDiff;
        }
    }

    int getNumGenerationsLived() const {
        return numEvaluations;
    }

    string toString() const {
        string ret("StartAgent\n");
        for (int i = KNOCKING; i <= END_DRAW; i++) {
            int ai = (i == END_DRAW) ? DRAWING : i;
            for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
                if (geneArray[i][j] != NULL) {
                    ret += geneArray[i][j]->toString() + "\n";
                }
            }
        }
        ret += "\nEndAgent";
        return ret;
    }

    string toShortString() const {
      string ret;
      for (int i = KNOCKING; i <= END_DRAW; i++) {
        int ai = (i == END_DRAW) ? DRAWING : i;
        for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
          if (geneArray[i][j] != NULL) {
            ret += geneArray[i][j]->toShortString() + "\n";
          }
        }
      }
      return ret;
    }

    string toCompactString() const {
        ostringstream retBuilder;
        for (int i = KNOCKING; i <= END_DRAW; i++) {
            int ai = (i == END_DRAW) ? DRAWING : i;
            for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
                if (geneArray[i][j] != NULL) {
                    retBuilder << "$" << geneArray[i][j]->toCompactString();
                }
            }
        }
        return retBuilder.str();
    }

    virtual ~AgentGenome() {
        for (int i = KNOCKING; i <= END_DRAW; i++) {
            int ai = (i == END_DRAW) ? DRAWING : i;
            for (int j = 0; j < AVAILABLE_STRATEGIES[ai]; j++) {
                if (geneArray[i][j] != NULL) {
                    delete geneArray[i][j]; // check this
                }
            }
        }
    }

};

#endif	/* _AGENTGENOME_H */

